#include <mips/regdef.h>


        .text                   # codigo en sector del texto
        .ent b64_encode         # declaro el nombre de la funcion
        .globl b64_encode       # declaro que sera global
b64_encode:
        .frame $fp, 40, ra      # defino mi frame
        sub sp, sp, 40          # sp apunta al final
        .cprestore 24           # salvo a gp
        sw $fp, 28(sp)          # salvo a fp
        sw ra, 32(sp)           # como es caller, salvo a ra
        move $fp, sp            # fp apunta donde apunta sp


	#       .beginProcess
        sw a0, 40($fp)          # recibo el puntero al buffer
        sw a1, 44($fp)          # recibo el puntero a los datos binarios
        sw a2, 48($fp)          # recibo el tamanio de esos datos

        beq a2, 1, a_byte       # la cadena de datos tiene 1 byte
        beq a2, 2, two_bytes    # la cadena de datos tiene 2 bytes
        beq a2, 3, three_bytes  # la cadena de datos tiene 3 bytes
        b err_length_inval      # la cadena de datos tiene long invalida
a_byte:
        lw t1, 44($fp)          # cargo el puntero de los datos
        lbu a0, 0(t1)           # cargo el primer byte de los datos
        jal b64_ascii_1         # busco el primer caracter de la base64
        lw t0, 40($fp)          # cargo el puntero del buffer
        sb v0, 0(t0)            # guardo el primer caracter de la base64

        lw t1, 44($fp)          # cargo el puntero de los datos
        lbu a0, 0(t1)           # cargo el primer byte de los datos
        jal b64_ascii_2_filled  # busco el segundo caracter de la base64
        lw t0, 40($fp)          # cargo el puntero del buffer
        sb v0, 1(t0)            # guardo el segundo caracter de la base64

        la t3, TBase64          # Cargo la dire de la talba
        lbu t3, 64(t3)          # Cargo el PADDING =
        sb t3, 2(t0)            # guardo el PADDING como tercer caracter
        sb t3, 3(t0)            # guardo el PADDING como cuarto caracter

        move v0, zero           # devuelvo 0 porque termino correctamente
        b return                # me voy de la funcion

two_bytes:
        lw t1, 44($fp)          # cargo el puntero de los datos
        lbu a0, 0(t1)           # cargo el primer byte de los datos
        jal b64_ascii_1         # busco el primer caracter de la base64
        lw t0, 40($fp)          # cargo el puntero del buffer
        sb v0, 0(t0)            # guardo el primer caracter de la base64

        lw t1, 44($fp)          # cargo el puntero de los datos
        lbu a0, 0(t1)           # cargo el primer byte de los datos
        lbu a1, 1(t1)           # cargo el segundo byte de los datos
        jal b64_ascii_2         # busco el segundo caracter de la base64
        lw t0, 40($fp)          # cargo el puntero del buffer
        sb v0, 1(t0)            # guardo el segundo caracter de la base64

        lw t1, 44($fp)          # cargo el puntero de los datos
        lbu a0, 1(t1)           # cargo el segundo byte de los datos
        jal b64_ascii_3_filled  # busco el tercer caracter de la base64
        lw t0, 40($fp)          # cargo el puntero del buffer
        sb v0, 2(t0)            # guardo el tercer caracter de la base64

        la t3, TBase64          # Cargo la dire de la talba
        lbu t3, 64(t3)          # Cargo el PADDING =
        sb t3, 3(t0)            # guardo el PADDING como cuarto caracter
        move v0, zero           # devuelvo 0 porque termino correctamente	
	b return

three_bytes:
        lw t1, 44($fp)          # cargo el puntero de los datos
        lbu a0, 0(t1)           # cargo el primer byte de los datos
        jal b64_ascii_1         # busco el primer caracter de la base64
        lw t0, 40($fp)          # cargo el puntero del buffer
        sb v0, 0(t0)            # guardo el primer caracter de la base64

        lw t1, 44($fp)          # cargo el puntero de los datos
        lbu a0, 0(t1)           # cargo el primer byte de los datos
        lbu a1, 1(t1)           # cargo el segundo byte de los datos
        jal b64_ascii_2         # busco el segundo caracter de la base64
        lw t0, 40($fp)          # cargo el puntero del buffer
        sb v0, 1(t0)            # guardo el segundo caracter de la base64

        lw t1, 44($fp)          # cargo el puntero de los datos
        lbu a0, 1(t1)           # cargo el segundo byte de los datos
        lbu a1, 2(t1)           # cargo el tercer byte de los datos
        jal b64_ascii_3         # busco el tercer caracter de la base64
        lw t0, 40($fp)          # cargo el puntero del buffer
        sb v0, 2(t0)            # guardo el tercer caracter de la base64

        lw t1, 44($fp)          # cargo el puntero de los datos
        lbu a0, 2(t1)           # cargo el tercer byte de los datos
        jal b64_ascii_4         # busco el cuarto caracter de la base64
        lw t0, 40($fp)          # cargo el puntero del buffer
        sb v0, 3(t0)            # guardo el cuarto caracter de la base64
        move v0, zero           # devuelvo 0 porque termino correctamente
        b return

err_length_inval:
        li v0, -2		# el tamanio de la cadena de datos es invalida
        #       .endProces
return:
        lw gp, 24(sp)           # restauro gp
        lw $fp, 28(sp)          # restauro fp
        lw ra, 32(sp)           # restauro ra
        addi sp, sp, 40         # restauro sp
        jr ra                   # retorno hacia la caller
        .end b64_encode         # fin b64_encode







        .text
        .ent    base64_get_value
        .globl  base64_get_value

base64_get_value:
        .frame $fp, 16, ra
        sub sp, sp, 16
        .cprestore 8
        sw $fp, 12(sp)
        move $fp, sp

        #       .beginProcess
        sw a0, 24($fp)    #guardo
        li t0, 64         #indice de la tabla
        la t2, TBase64    #Guardo la dire de la talba
test_1: bltz t0, exit_err #no encontre el caracter
        add t3, t0, t2    #Obtengo la dire del elem_i
        lbu t3,0(t3)      #Cargo el elem_i
        sub t3, t3, a0    #si son iguales da 0
        beqz t3, exit     #si da 0 me voy
        addi t0, t0, -1   #resto 1 para el proximo byte
        b test_1
exit:   move v0, t0       #encontre el indice y lo guardo en v0
        b return_get_value
exit_err:li v0, -1
        #       .endProces
return_get_value:
        lw gp, 8(sp)
        lw $fp, 12(sp)
	addi sp, sp, 16
        jr ra

        .end base64_get_value





        .text
        .ent b64_ascii_1
        .globl b64_ascii_1

b64_ascii_1:
        .frame $fp, 16, ra
        sub sp, sp, 16
        #sw gp, 8(sp)
        .cprestore 8
	sw $fp, 12(sp)
        move $fp, sp

        #       .beginProcess
        sw a0, 16($fp)
        srl t0, a0, 2
        li t1, 0x3f       # cargo -> B64_MASK_SET_2_UP
        and t1, t0, t1    # Nro & B64_MASK_SET_2_UP
        la t2, TBase64    # Cargo la dire del venctor
        add t1, t1, t2    # obtengo el offset
        lbu v0, 0(t1)     # cargo el valor en v0
        #       .endProcess
#return:
        lw gp, 8(sp)
        lw $fp, 12(sp)
	addi sp, sp, 16
        jr ra

        .end b64_ascii_1





        .text
        .ent b64_ascii_2
        .globl b64_ascii_2

b64_ascii_2:
        .frame $fp, 16, ra
        sub sp, sp, 16
        #sw gp, 8(sp)
	.cprestore 8        
	sw $fp, 12(sp)
        move $fp, sp

        #       .beginProcess
        sw a0, 16($fp)
        sw a1, 20($fp)
        li t0, 0x03     # cargo-> B64_MASK_SET_6_UP
        and t0, a0, t0  #c1 & B64_MASK_SET_6_UP
        sll t0, t0, 4   #Nro << 4
        li t1, 0xf0     # cargo-> B64_MASK_SET_4_BW
        and t0, t0, t1  #Nro & B64_MASK_SET_4_BW
        srl t1, a1, 4   # c2 >> 4
        li t2, 0x0f     # cargo ->B64_MASK_SET_4_UP
	and t1, t1, t2  # aux & B64_MASK_SET_4_UP
        or t0, t0, t1
        la t2, TBase64  # Cargo la dire del venctor
        add t0, t0, t2  # obtengo el offset
        lbu v0, 0(t0)   # cargo el valor en v0
        #       .endProcess
#return:
        lw gp, 8(sp)
        lw $fp, 12(sp)
	addi sp, sp, 16
        jr ra

        .end b64_ascii_2





        .text
        .ent b64_ascii_3
        .globl b64_ascii_3

b64_ascii_3:
        .frame $fp, 16, ra
        sub sp, sp, 16
        #sw gp, 8(sp)
	.cprestore 8        
	sw $fp, 12(sp)
        move $fp, sp

        #       .beginProcess
        sw a0, 16($fp)
        sw a1, 20($fp)
        li t0, 0x0f        # cargo-> B64_MASK_SET_4_UP
        and t0, a0, t0     # c1 & B64_MASK_SET_4_UP
        sll t0, t0, 2      # Nro << 2
        li t1, 0xfc        # cargo-> B64_MASK_SET_2_BW
        and t0, t0, t1     # Nro & B64_MASK_SET_2_BW
        srl t1, a1, 6      # c2 >> 6
        li t2, 0x03        # cargo ->B64_MASK_SET_6_UP
        and t1, t1, t2     # aux & B64_MASK_SET_6_UP
        or t0, t0, t1
        la t2, TBase64     # Cargo la dire del venctor
        add t0, t0, t2     # obtengo el offset
        lbu v0, 0(t0)      # cargo el valor en v0
        #       .endProcess
#return:
        lw gp, 8(sp)
        lw $fp, 12(sp)
        addi sp, sp, 16
	jr ra

        .end b64_ascii_3





        .text
        .ent b64_ascii_4
        .globl b64_ascii_4

b64_ascii_4:
        .frame $fp, 16, ra
        sub sp, sp, 16
        #sw gp, 8(sp)
	.cprestore 8        
	sw $fp, 12(sp)
        move $fp, sp

        #       .beginProcess
        sw a0, 16($fp)
        li t1, 0x3f           # cargo -> B64_MASK_SET_2_UP
        and t1, a0, t1        # Nro & B64_MASK_SET_2_UP
        la t2, TBase64        # Cargo la dire del venctor
        add t1, t1, t2        # obtengo el offset
        lbu v0, 0(t1)         # cargo el valor en v0
        #       .endProcess
#return:
        lw gp, 8(sp)
        lw $fp, 12(sp)
	addi sp, sp, 16        
	jr ra

        .end b64_ascii_4




        .text
        .ent b64_ascii_2_filled
        .globl b64_ascii_2_filled

b64_ascii_2_filled:
        .frame $fp, 16, ra
        sub sp, sp, 16
        #sw gp, 8(sp)
	.cprestore 8
        sw $fp, 12(sp)
        move $fp, sp

        #       .beginProcess
        sw a0, 16($fp)
        sll t0, a0, 4      # Nro << 4
        li t1, 0x3f        # cargo-> B64_MASK_SET_2_UP
        and t0, t0, t1     # Nro & B64_MASK_SET_2_UP
        li t1, 0xf0        # cargo-> B64_MASK_SET_4_BW
        and t0, t0, t1     # Nro & B64_MASK_SET_2_BW
        la t2, TBase64     # Cargo la dire del venctor
        add t0, t0, t2     # obtengo el offset
        lbu v0, 0(t0)      # cargo el valor en v0
        #       .endProcess
#return:
        lw gp, 8(sp)
        lw $fp, 12(sp)
	addi sp, sp, 16        
	jr ra

        .end b64_ascii_2_filled



        .text
        .ent b64_ascii_3_filled
        .globl b64_ascii_3_filled

b64_ascii_3_filled:
        .frame $fp, 16, ra
        sub sp, sp, 16
        #sw gp, 8(sp)
	.cprestore 8        
	sw $fp, 12(sp)
        move $fp, sp

        #       .beginProcess
        sw a0, 16($fp)
        sll t0, a0, 2      # Nro << 2
        li t1, 0x3f        # cargo-> B64_MASK_SET_2_UP
        and t0, t0, t1     # Nro & B64_MASK_SET_2_UP
        li t1, 0xfc        # cargo-> B64_MASK_SET_2_BW
        and t0, t0, t1     # Nro & B64_MASK_SET_2_BW
        la t2, TBase64     # Cargo la dire del venctor
        add t0, t0, t2     # obtengo el offset
        lbu v0, 0(t0)      # cargo el valor en v0
        #       .endProcess
#return:
        lw gp, 8(sp)
        lw $fp, 12(sp)
	addi sp, sp, 16
	jr ra

        .end b64_ascii_3_filled




        .globl TBase64
        .rdata
        .align 2
TBase64:
        .byte   'A', 'B', 'C', 'D', 'E'
        .byte   'F', 'G', 'H', 'I', 'J'
        .byte   'K', 'L', 'M', 'N', 'O'
        .byte   'P', 'Q', 'R', 'S', 'T'
        .byte   'U', 'V', 'W', 'X', 'Y'
        .byte   'Z', 'a', 'b', 'c', 'd'
        .byte   'e', 'f', 'g', 'h', 'i'
        .byte   'j', 'k', 'l', 'm', 'n'
        .byte   'o', 'p', 'q', 'r', 's'
        .byte   't', 'u', 'v', 'w', 'x'
        .byte   'y', 'z', '0', '1', '2'
        .byte   '3', '4', '5', '6', '7'
        .byte   '8', '9', '+', '/', '='


